using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using mp.Scanner;
using mp.Models;

namespace mp
{
	public class Driver
	{		
		public Driver()
		{
			this.scanner = new mp.Scanner.Scanner();
		}
		
		public void Run(string filename, string source)
		{			
			//if(File.Exists("symbolTable.txt"))
			//	File.Delete("symbolTable.txt");			
			//writer1 = new StreamWriter("symbolTable.txt");
		    	
			this.analyzer = new mp.Analyzer.Analyzer(source);
			this.scanner.OpenFile(filename);			
			currentToken = this.scanner.GetToken();			
			this.SystemGoal();
						
			//writer1.Close();
		}
		

		#region parser methods
		
		private void ActualParameter(Parameter parameter){
			switch(currentToken){
				case Token.mp_lparen:
				case Token.mp_plus:
				case Token.mp_minus:
				case Token.mp_identifier:
				case Token.mp_integer_lit:
				case Token.mp_not:		
					if(parameter.Mode == ParameterMode.Variable.GetHashCode())
					{
						string varName = "";
						VariableIdentifier(ref varName);
						analyzer.gen_PushVariableParameter(topTable, parameter, varName);
					}
					else
					{
						int type = 0;
						OrdinalExpression(ref type);
						if(type != parameter.Type)
						{
							Console.WriteLine("Parameter types do not match on line {0}", scanner.GetLine());
						}
					}					
					break;
				default:
					Console.WriteLine("Parse error: Expected '(', '+', '-', an identifier, a number or 'not' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void ActualParameterTail(List<Parameter> parameters){
			switch(currentToken){
				case Token.mp_comma:
					Match(Token.mp_comma);
					if(parameters.Count > 0)
					{
						ActualParameter(parameters[0]);
					}
					else
					{
						Console.WriteLine("Too many parameters in procedure or function call at line {0}", scanner.GetLine());
					}
				
					if(parameters.Count > 1)
						ActualParameterTail(parameters.GetRange(1, parameters.Count - 1));
					else
						ActualParameterTail(new List<Parameter>());				
					break;
				case Token.mp_rparen:	
					break;
				default:
					Console.WriteLine("Parse error: Expected ',' or ')' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void AddingOperator(ref Semantic_rec addingOp_rec){
			switch(currentToken){
				case Token.mp_plus:
					addingOp_rec.AddingOp = AddingOp.Plus.GetHashCode();
					Match(Token.mp_plus);
					break;
				case Token.mp_minus:
					addingOp_rec.AddingOp = AddingOp.Minus.GetHashCode();
					Match(Token.mp_minus);
					break;
				case Token.mp_or:
					addingOp_rec.AddingOp = AddingOp.Or.GetHashCode();
					Match(Token.mp_or);
					break;
				default:
					Console.WriteLine("Parse error: Expected '+', '-' or 'or' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void AssignmentStatement(){
			switch(currentToken){
				case Token.mp_identifier:
					string iden_rec = "";
					if(topTable.GetSymbolKind(scanner.GetLexeme()) == SymbolKind.Function.GetHashCode())
					{						
						FunctionIdentifier(ref iden_rec);
						Match(Token.mp_assign);						
						Semantic_rec expression_rec = new Semantic_rec();
						Expression(ref expression_rec);
						analyzer.gen_FunctionAssignment(topTable, iden_rec, expression_rec);
					}
					else if(topTable.GetSymbolKind(scanner.GetLexeme()) == SymbolKind.Variable.GetHashCode() || topTable.GetSymbolKind(scanner.GetLexeme()) == SymbolKind.Parameter.GetHashCode())
					{
						VariableIdentifier(ref iden_rec);											
						Match(Token.mp_assign);					
						Semantic_rec expression_rec = new Semantic_rec();
						Expression(ref expression_rec);					
						analyzer.gen_Assignment(topTable, iden_rec, expression_rec);
					}
					else
						throw new Exception("Expected different Identifier.");
					break;
				default:
					Console.WriteLine("Parse error: Expected 'procedure' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void Block(string startLabel){
			switch(currentToken){
				case Token.mp_begin:
				case Token.mp_function:
				case Token.mp_procedure:
				case Token.mp_var:
					VariableDeclarationPart();
					int varDecSize = topTable.GetVariableCount();
					ProcedureAndFunctionDeclarationPart();
					analyzer.gen_Label(startLabel);
					analyzer.gen_VarDecleration(varDecSize);
					StatementPart();
					analyzer.gen_BlockEnd(topTable.GetVariableCount());
					//PrintTable();
					break;
				default:
					Console.WriteLine("Parse error: Expected 'begin', 'function', 'procedure' or 'var' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void BooleanExpression(){
			switch(currentToken){
				case Token.mp_lparen:
				case Token.mp_plus:
				case Token.mp_minus:
				case Token.mp_identifier:
				case Token.mp_integer_lit:
				case Token.mp_not:
					Semantic_rec expression_rec = new Semantic_rec();
					Expression(ref expression_rec);
					break;
				default:
					Console.WriteLine("Parse error: Expected '(', '+', '-', an identifier, a number or 'not' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void CompoundStatement(){
			switch(currentToken){
				case Token.mp_begin:
					Match (Token.mp_begin);
					StatementSequence();
					Match (Token.mp_end);
					break;
				default:
					Console.WriteLine("Parse error: Expected 'begin' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void ControlVariable(ref string controlVariable){
			switch(currentToken){
				case Token.mp_identifier:
					VariableIdentifier(ref controlVariable);
					break;
				default:
					Console.WriteLine("Parse error: Expected an identifier but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void EmptyStatement(){
			switch(currentToken){
				case Token.mp_scolon:
				case Token.mp_else:
				case Token.mp_end:
				case Token.mp_until:
					break;
				default:
					Console.WriteLine("Parse error: Expected ';', 'else', 'end' or 'until' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void Expression(ref Semantic_rec expression_rec){
			switch(currentToken){
				case Token.mp_lparen:
				case Token.mp_plus:
				case Token.mp_minus:
				case Token.mp_identifier:
				case Token.mp_not:
				case Token.mp_integer_lit:
					SimpleExpression(ref expression_rec); 
					OptionalRelationalPart(expression_rec);
					break;
				default:
					Console.WriteLine("Parse error: Expected '(', '+', '-', an identifier, 'not' or a number but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
	
		private void Factor(ref Semantic_rec expression_rec){
			switch(currentToken){
				case Token.mp_integer_lit:
					analyzer.gen_PushNum(scanner.GetLexeme());
					Match(Token.mp_integer_lit);
					expression_rec.Type = SymbolTypes.Integer.GetHashCode();
					break;
				case Token.mp_identifier:
					Symbol tempSymbol = topTable.GetSymbol(scanner.GetLexeme());
					if(tempSymbol.Kind == SymbolKind.Function.GetHashCode())
					{
						analyzer.gen_PreFunctionCall();
						ProcedureOrFunction_rec function_rec = new ProcedureOrFunction_rec();
						FunctionIdentifier(ref function_rec.identifier);
						OptionalActualParameterList(tempSymbol.Params);
						analyzer.gen_SetRegister(tempSymbol.Params.Count, tempSymbol.Level + 1);
						analyzer.gen_FunctionCallAndCleanUp(tempSymbol, function_rec, tempSymbol.Level + 1);
						expression_rec.Type = tempSymbol.Type;
					}
					else if(tempSymbol.Kind == SymbolKind.Variable.GetHashCode() || tempSymbol.Kind == SymbolKind.Parameter.GetHashCode())
					{
						string temp = "";
						VariableIdentifier(ref temp);
						analyzer.gen_PushVar(tempSymbol);
						expression_rec.Type = tempSymbol.Type;
					}
					else
						throw new Exception("Expected different identifier");
					break;
				case Token.mp_not:
					Match(Token.mp_not);
					Factor(ref expression_rec);
					analyzer.gen_Not();
					break;
				case Token.mp_lparen:;
					Match(Token.mp_lparen);
					Expression(ref expression_rec);
					Match(Token.mp_rparen);
					break;
				default:
					Console.WriteLine("Parse error: Expected a number, an identifier, 'not' or '(' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void FactorTail(ref Semantic_rec expression_rec){
			switch(currentToken){
				case Token.mp_times:
				case Token.mp_and:
				case Token.mp_div:
				case Token.mp_mod:
					Semantic_rec multiplyOp_rec = new Semantic_rec();
					MultiplyingOperator(ref multiplyOp_rec);					
					Factor(ref expression_rec);
					analyzer.gen_MultiplyingOp(multiplyOp_rec);
					Semantic_rec factorTail_rec = new Semantic_rec();
					FactorTail(ref factorTail_rec);
					
					if(factorTail_rec.Type == SymbolTypes.Undefined.GetHashCode() || expression_rec.Type == factorTail_rec.Type)
					{}	
					else
						Console.WriteLine("Type mismatch error on line {0}", scanner.GetLine());
					break;
				case Token.mp_comma:
				case Token.mp_scolon:
				case Token.mp_rparen:
				case Token.mp_equal:
				case Token.mp_lthan:
				case Token.mp_gthan:
				case Token.mp_gequal:
				case Token.mp_lequal:
				case Token.mp_nequal:
				case Token.mp_plus:
				case Token.mp_minus:
				case Token.mp_do:
				case Token.mp_downto:
				case Token.mp_else:
				case Token.mp_end:
				case Token.mp_or:
				case Token.mp_then:
				case Token.mp_to:
				case Token.mp_until:
					expression_rec.Type = SymbolTypes.Undefined.GetHashCode();
					break;
				default:
					Console.WriteLine("Parse error: Expected multiplying operator, ',', ';', a relational operator, '+', '-' or a start of statement but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void FinalValue(){
			switch(currentToken){
				case Token.mp_lparen:
				case Token.mp_plus:
				case Token.mp_minus:
				case Token.mp_identifier:
				case Token.mp_integer_lit:
				case Token.mp_not:
					int temp = 0;
					OrdinalExpression(ref temp);
					break;
				default:
					Console.WriteLine("Parse error: Expected '(', '+', '-', an identifier, a number or 'not' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void FormalParameterSection(ref ProcOrFuncObject procOrFuncObject){
			ParamObject parameter;
			
			switch(currentToken){
				case Token.mp_identifier:
					parameter = new ParamObject();
					parameter.IsValueParam = true;
					ValueParameterSection(ref parameter);
					procOrFuncObject.Params.Add(parameter);
					break;
				case Token.mp_var:
					parameter = new ParamObject();
					parameter.IsValueParam = false;				
					VariableParameterSection(ref parameter);
					procOrFuncObject.Params.Add(parameter);
					break;
				default:
					Console.WriteLine("Parse error: Expected an identifier or 'var' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void FormalParameterSectionTail(ref ProcOrFuncObject procOrFuncObject){
			switch(currentToken){
				case Token.mp_scolon:
					Match(Token.mp_scolon);
					FormalParameterSection(ref procOrFuncObject);
					FormalParameterSectionTail(ref procOrFuncObject);
					break;
				case Token.mp_rparen:
					break;
				default:
					Console.WriteLine("Parse error: Expected ';' or ')' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void ForStatement(){
			switch(currentToken){
				case Token.mp_for:
					Match(Token.mp_for);
					Symbol controlSymbol = new Symbol();
					var controlVariable = "";
					var topOfLoop = "";
					var endLabel = "";
					ControlVariable(ref controlVariable);
					Match(Token.mp_assign);
					InitialValue();
					analyzer.gen_ForStatementStart(topTable, controlVariable, ref topOfLoop, ref controlSymbol);
					StepValue_rec stepValue_rec = new StepValue_rec();
					StepValue(ref stepValue_rec);
					FinalValue();
					analyzer.gen_ForStatementCheck(ref endLabel, stepValue_rec, topOfLoop, controlSymbol);
					Match(Token.mp_do);
					Statement();
					analyzer.gen_ForStatementEnd(topOfLoop, endLabel, stepValue_rec, controlSymbol);
					break;
				default:
					Console.WriteLine("Parse error: Expected 'for' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void FunctionDeclaration(){
			switch(currentToken){
				case Token.mp_function:
					ProcOrFuncObject funcObject = new ProcOrFuncObject();
					FunctionHeading(ref funcObject);
					string label = analyzer.newLabel();
					NewFunctionOrProcedureTable(funcObject, label);
					Match(Token.mp_scolon);
					Block(label);
					Match(Token.mp_scolon);
					analyzer.gen_ProcedureOrFunctionRet();
					topTable = topTable.prevTable;
					break;
				default:
					Console.WriteLine("Parse error: Expected 'function' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void FunctionHeading(ref ProcOrFuncObject funcObject){
			switch(currentToken){
				case Token.mp_function:
					Match(Token.mp_function);
					funcObject.ProcedurOrFunction = SymbolKind.Function.GetHashCode();
					FunctionIdentifier(ref funcObject.ProcedureOrFunctionId);
					OptionalFormalParameterList(ref funcObject);
					Match(Token.mp_colon);
					Type(ref funcObject.Type);
					break;
				default:
					Console.WriteLine("Parse error: Expected 'function' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void FunctionIdentifier(ref string function_rec){
			switch(currentToken){
				case Token.mp_identifier:
					function_rec = scanner.GetLexeme();
					Match(Token.mp_identifier);
					break;
				default:
					Console.WriteLine("Parse error: Expected 'function' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void IdentifierList(ref ArrayList idList){
			switch(currentToken){
				case Token.mp_identifier:
					idList.Add(scanner.GetLexeme());
					Match(Token.mp_identifier);
					IdentifierTail(ref idList);
					break;
				default:
					Console.WriteLine("Parse error: Expected an identifier but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void IdentifierTail(ref ArrayList idList){
			switch(currentToken){
				case Token.mp_comma:
					Match(Token.mp_comma);
					idList.Add(scanner.GetLexeme());
					Match(Token.mp_identifier);
					IdentifierTail(ref idList);
					break;
				case Token.mp_colon:
					break;
				default:
					Console.WriteLine("Parse error: Expected ',' or ':' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void IfStatement(){
			switch(currentToken){
				case Token.mp_if:
					Match(Token.mp_if);
					BooleanExpression();
					Match(Token.mp_then);				
					var elseLabel = "";
					var finishLabel = "";
					analyzer.gen_IfCheck(ref elseLabel, ref finishLabel);				
					Statement();
					analyzer.gen_ElsePart(finishLabel, elseLabel);
					OptionalElsePart();
					analyzer.gen_Label(finishLabel);
					break;
				default:
					Console.WriteLine("Parse error: Expected 'if' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void InitialValue(){
			switch(currentToken){
				case Token.mp_lparen:
				case Token.mp_plus:
				case Token.mp_minus:
				case Token.mp_identifier:
				case Token.mp_integer_lit:
				case Token.mp_not:
					int temp = 0;
					OrdinalExpression(ref temp);
					break;
				default:
					Console.WriteLine("Parse error: Expected '(', '+', '-', an identifier, a number or 'not' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void MultiplyingOperator(ref Semantic_rec multiplyingOp_rec){
			switch(currentToken){
				case Token.mp_times:
					multiplyingOp_rec.MultiplyingOp = MultiplyingOp.Multiply.GetHashCode();
					Match(Token.mp_times);
					break;
				case Token.mp_div:
					multiplyingOp_rec.MultiplyingOp = MultiplyingOp.Divide.GetHashCode();
					Match(Token.mp_div);
					break;
				case Token.mp_mod:
					multiplyingOp_rec.MultiplyingOp = MultiplyingOp.Mod.GetHashCode();
					Match(Token.mp_mod);
					break;
				case Token.mp_and:
					multiplyingOp_rec.MultiplyingOp = MultiplyingOp.And.GetHashCode();
					Match(Token.mp_and);
					break;
				default:
					Console.WriteLine("Parse error: Expected '*', 'div', 'mod' or 'and' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void OptionalActualParameterList(List<Parameter> parameters){
			switch(currentToken){
				case Token.mp_lparen:
					Match(Token.mp_lparen);
					//int type = 0;
					if(parameters.Count > 0)
					{
						ActualParameter(parameters[0]);
					}
					else
					{
						Console.WriteLine("Too many parameters in procedure or function call at line {0}", scanner.GetLine());
					}
				
					if(parameters.Count > 1)
						ActualParameterTail(parameters.GetRange(1, parameters.Count - 1));
					else
						ActualParameterTail(new List<Parameter>());
					//parameterTypes.Add(type);					
					Match(Token.mp_rparen);
					break;
				case Token.mp_scolon:
				case Token.mp_rparen:
				case Token.mp_equal:
				case Token.mp_lthan:
				case Token.mp_gthan:
				case Token.mp_gequal:
				case Token.mp_lequal:
				case Token.mp_not:
				case Token.mp_plus:
				case Token.mp_minus:
				case Token.mp_times:
				case Token.mp_and:
				case Token.mp_div:
				case Token.mp_do:
				case Token.mp_downto:
				case Token.mp_else:
				case Token.mp_end:
				case Token.mp_integer_lit:
				case Token.mp_or:
				case Token.mp_then:
				case Token.mp_to:
				case Token.mp_until:
					break;
				default:
					Console.WriteLine("Parse error: Expected start of statement, multiplying operator, relational operator, ';', ')', '(', '+', or '-' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void OptionalElsePart(){
			switch(currentToken){
				case Token.mp_else:
					Match(Token.mp_else);
					Statement();
					break;
				case Token.mp_scolon:
				case Token.mp_end:
				case Token.mp_until:
					break;
				default:
					Console.WriteLine("Parse error: Expected 'else', ';', 'end' or 'until' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void OptionalFormalParameterList(ref ProcOrFuncObject procOrFuncObject){
			switch(currentToken){
				case Token.mp_lparen:
					Match(Token.mp_lparen);
					FormalParameterSection(ref procOrFuncObject);
					FormalParameterSectionTail(ref procOrFuncObject);
					Match(Token.mp_rparen);
					break;
				case Token.mp_scolon:
				case Token.mp_float:
				case Token.mp_integer:
				case Token.mp_colon:
					break;
				default:
					Console.WriteLine("Parse error: Expected '(', ';', 'Float' or 'Integer' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void OptionalRelationalPart(Semantic_rec leftOp_rec){
			switch(currentToken){
				case Token.mp_equal:
				case Token.mp_lthan:
				case Token.mp_gthan:
				case Token.mp_lequal:
				case Token.mp_gequal:
				case Token.mp_nequal:
					Semantic_rec relationalOp_rec = new Semantic_rec();
					RelationalOperator(ref relationalOp_rec);
				
					Semantic_rec expression_rec = new Semantic_rec();
					SimpleExpression(ref expression_rec);
					
					if(expression_rec.Type == leftOp_rec.Type)
						analyzer.gen_RelationalOp(relationalOp_rec);
					else
						Console.WriteLine("Type mismatch error on line {0} in relational operation", scanner.GetLine());
				
					break;
				case Token.mp_comma:
				case Token.mp_scolon:
				case Token.mp_rparen:
				case Token.mp_do:
				case Token.mp_downto:
				case Token.mp_else:
				case Token.mp_end:
				case Token.mp_then:
				case Token.mp_to:
				case Token.mp_until:
					break;
				default:
					Console.WriteLine("Parse error: Expected start of statement, multiplying operator, relational operator, ';', ')', '+', ',' or '-' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void OptionalSign(ref Sign_rec opSign_rec){
			switch(currentToken){
				case Token.mp_plus:
					Match(Token.mp_plus);
					break;
				case Token.mp_minus:
					opSign_rec.negate = true;
					Match(Token.mp_minus);
					break;
				case Token.mp_lparen:
				case Token.mp_identifier:
				case Token.mp_integer_lit:
				case Token.mp_not:
					break;
				default:
					Console.WriteLine("Parse error: Expected '+', '-', '(', an identifier, a number or 'not' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void OrdinalExpression(ref int Type){
			switch(currentToken){
				case Token.mp_lparen:
				case Token.mp_identifier:
				case Token.mp_plus:
				case Token.mp_minus:
				case Token.mp_integer_lit:
				case Token.mp_or:
					Semantic_rec expression_rec = new Semantic_rec();
					Expression(ref expression_rec);
					Type = expression_rec.Type;
					break;
				default:
					Console.WriteLine("Parse error: Expected 'procedure' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void ProcedureAndFunctionDeclarationPart(){
			switch(currentToken){
				case Token.mp_procedure:
					ProcedureDeclaration();
					ProcedureAndFunctionDeclarationPart();
					break;
				case Token.mp_function:
					FunctionDeclaration();
					ProcedureAndFunctionDeclarationPart();
					break;
				case Token.mp_begin:
					break;
				default:
					Console.WriteLine("Parse error: Expected 'procedure' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void ProcedureDeclaration(){
			switch(currentToken){
				case Token.mp_procedure:
					ProcOrFuncObject procObject = new ProcOrFuncObject();
					ProcedureHeading(ref procObject);
					string label = analyzer.newLabel();
					NewFunctionOrProcedureTable(procObject, label);	
					Match(Token.mp_scolon);
					Block(label);
					Match(Token.mp_scolon);
					analyzer.gen_ProcedureOrFunctionRet();
					topTable = topTable.prevTable;
					break;
				default:
					Console.WriteLine("Parse error: Expected 'procedure' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void ProcedureHeading(ref ProcOrFuncObject procObject){
			switch(currentToken){
				case Token.mp_procedure:
					Match(Token.mp_procedure);
					procObject.ProcedurOrFunction = SymbolKind.Procedure.GetHashCode();
					ProcedureIdentifier(ref procObject.ProcedureOrFunctionId);
					OptionalFormalParameterList(ref procObject);
					break;
				default:
					Console.WriteLine("Parse error: Expected 'procedure' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void ProcedureIdentifier(ref string identifier){
			switch(currentToken){
				case Token.mp_identifier:
					identifier = scanner.GetLexeme();
					Match(Token.mp_identifier);
					break;
				default:
					Console.WriteLine("Parse error: Expected 'procedure' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}

		private void ProcedureStatement(){
			switch(currentToken){
				case Token.mp_identifier:
					analyzer.gen_PreProcedureCall();
					ProcedureOrFunction_rec procedure_rec = new ProcedureOrFunction_rec();
					ProcedureIdentifier(ref procedure_rec.identifier);
					Symbol procSymbol = topTable.GetSymbol(procedure_rec.identifier);
					OptionalActualParameterList(procSymbol.Params);
					analyzer.gen_SetRegister(procSymbol.Params.Count, procSymbol.Level + 1);
					analyzer.gen_ProcedureCallAndCleanUp(topTable, procedure_rec);
					break;
				default:
					Console.WriteLine("Parse error: Expected an identifier but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void Program(){
			switch(currentToken){
				case Token.mp_program:
					string mainStartLabel = analyzer.newLabel();
					analyzer.gen_ProgStart(mainStartLabel);
					ProgramHeading();
					Match(Token.mp_scolon);
					Block(mainStartLabel);
					Match(Token.mp_period);
					analyzer.gen_ProgEnd(topTable.GetSize());
					break;
				default:
					Console.WriteLine("Parse error: Expected 'program' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void ProgramHeading(){
			switch(currentToken){
				case Token.mp_program:
					Match(Token.mp_program);
					string name = "";
					ProgramIdentifier(ref name);					
					topTable = new SymbolTable();
					topTable.Name = name;			
					break;
				default:
					Console.WriteLine("Parse error: Expected 'program' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void ProgramIdentifier(ref string progName){
			switch(currentToken){
				case Token.mp_identifier:
					progName = scanner.GetLexeme();
					Match(Token.mp_identifier);
					break;
				default:
					Console.WriteLine("Parse error: Expected an identifier but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void ReadParameter(ref Ident_rec record){
			switch(currentToken){
				case Token.mp_identifier:
					var identifier = "";
					VariableIdentifier(ref identifier);
					record.identifier_List.Add(identifier);
					break;
				default:
					Console.WriteLine("Parse error: Expected an identifier but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void ReadParameterTail(ref Ident_rec record){
			switch(currentToken){
				case Token.mp_comma:
					Match(Token.mp_comma);
					ReadParameter(ref record);
					ReadParameterTail(ref record);
					break;
				case Token.mp_rparen:
					break;
				default:
					Console.WriteLine("Parse error: Expected ',' or ')' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void ReadStatement(){
			switch(currentToken){
				case Token.mp_read:
					Match(Token.mp_read);
					Match(Token.mp_lparen);
				
					Ident_rec record = new Ident_rec();
				
					ReadParameter(ref record);
					ReadParameterTail(ref record);
					Match (Token.mp_rparen);
				
					analyzer.gen_ReadStatement(topTable, record);	
				
					break;
				default:
					Console.WriteLine("Parse error: Expected 'read' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void RelationalOperator(ref Semantic_rec relationalOp_rec){
			switch(currentToken){
				case Token.mp_equal:
					relationalOp_rec.RelationalOp = RelationalOp.Equal.GetHashCode();
					Match(Token.mp_equal);
					break;
				case Token.mp_lthan:
					relationalOp_rec.RelationalOp = RelationalOp.LessThan.GetHashCode();
					Match(Token.mp_lthan);
					break;
				case Token.mp_gthan:
					relationalOp_rec.RelationalOp = RelationalOp.GreaterThan.GetHashCode();
					Match(Token.mp_gthan);
					break;
				case Token.mp_lequal:
					relationalOp_rec.RelationalOp = RelationalOp.LessThanE.GetHashCode();
					Match(Token.mp_lequal);
					break;
				case Token.mp_gequal:
					relationalOp_rec.RelationalOp = RelationalOp.GreaterThanE.GetHashCode();
					Match(Token.mp_gequal);
					break;
				case Token.mp_nequal:
					relationalOp_rec.RelationalOp = RelationalOp.NotEqual.GetHashCode();
					Match(Token.mp_nequal);
					break;
				default:
					Console.WriteLine("Parse error: Expected '=', '<', '>', '<=', '>=' or '<>' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void RepeatStatement(){
			switch(currentToken){
				case Token.mp_repeat:;
					Match(Token.mp_repeat);					
					var repeatStartLabel = "";
					analyzer.gen_RepeatStart(ref repeatStartLabel);
					StatementSequence();
					Match(Token.mp_until);
					BooleanExpression();
					analyzer.gen_RepeatCheck(repeatStartLabel);
					break;
				default:
					Console.WriteLine("Parse error: Expected 'procedure' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void SimpleExpression(ref Semantic_rec expression_rec){
			switch(currentToken){
				case Token.mp_integer_lit:
				case Token.mp_lparen:
				case Token.mp_plus:
				case Token.mp_minus:
				case Token.mp_identifier:
				case Token.mp_not:
					Sign_rec opSign_rec = new Sign_rec();
					OptionalSign(ref opSign_rec);
					Term(ref expression_rec);
					if(opSign_rec.negate)
							analyzer.gen_NegSign();
					Semantic_rec termTail_rec = new Semantic_rec();
					TermTail(ref termTail_rec);
				
					if(termTail_rec.Type == SymbolTypes.Undefined.GetHashCode() || expression_rec.Type == termTail_rec.Type)
					{}
					else
					{
						Console.WriteLine("Type mismatch error on line {0}", scanner.GetLine());
					}
					
					break;
				default:
					Console.WriteLine("Parse error: Expected '(', '+', '-', an identifier, 'not' or a number but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void Statement(){
			switch(currentToken){
				case Token.mp_scolon:
				case Token.mp_else:
				case Token.mp_end:
				case Token.mp_until:
					EmptyStatement();
					break;
				case Token.mp_begin:
					CompoundStatement();
					break;
				case Token.mp_read:
					ReadStatement();
					break;
				case Token.mp_write:
					WriteStatement();
					break;			
				case Token.mp_identifier:
					if(topTable.GetSymbolKind(scanner.GetLexeme()) == SymbolKind.Procedure.GetHashCode())
					{
						ProcedureStatement();
					}
					else //if(topTable.GetSymbolKind(scanner.GetLexeme()) == SymbolKind.Variable.GetHashCode() || topTable.GetSymbolKind(scanner.GetLexeme()) == SymbolKind.Parameter.GetHashCode())
					{
						AssignmentStatement();
					}
					//else
					//	throw new Exception("Wrong type of identifier");
					break;
				case Token.mp_if:
					IfStatement();
					break;
				case Token.mp_while:
					WhileStatement();
					break;
				case Token.mp_repeat:
					RepeatStatement();
					break;
				case Token.mp_for:
					ForStatement();
					break;
				default:
					Console.WriteLine("Parse error: Expected beginining of statement or an identifier but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void StatementPart(){
			switch(currentToken){
				case Token.mp_begin:
					CompoundStatement();
					break;
				default:
					Console.WriteLine("Parse error: Expected 'begin' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void StatementSequence(){
			switch(currentToken){
				case Token.mp_scolon:
				case Token.mp_begin:
				case Token.mp_else:
				case Token.mp_end:
				case Token.mp_for:
				case Token.mp_identifier:
				case Token.mp_if:
				case Token.mp_read:
				case Token.mp_repeat:
				case Token.mp_until:
				case Token.mp_while:
				case Token.mp_write:
					Statement();
					StatementTail();
					break;
				default:
					Console.WriteLine("Parse error: Expected ';', 'begin', 'else', 'end', 'for', an identifier, 'if', 'read', 'repeat', 'until', 'while' or 'write' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void StatementTail(){
			switch(currentToken){
				case Token.mp_scolon:
					Match (Token.mp_scolon);
					Statement();
					StatementTail();
					break;
				case Token.mp_end:
				case Token.mp_until:
					break;
				default:
					Console.WriteLine("Parse error: Expected ';', 'end' or 'until' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void StepValue(ref StepValue_rec stepValue_rec){
			switch(currentToken){
				case Token.mp_to:
					Match(Token.mp_to);
					break;
				case Token.mp_downto:
					stepValue_rec.downto = true;
					Match(Token.mp_downto);
					break;
				default:
					Console.WriteLine("Parse error: Expected 'to' or 'downto' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void SystemGoal()
		{
			
			switch(currentToken){
				case Token.mp_program:
					Program();
					Match(Token.mp_eof);
					break;
				default:
					Console.WriteLine("Parse error: Expected 'program' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void Term(ref Semantic_rec expression_rec){
			switch(currentToken){
				case Token.mp_lparen:
				case Token.mp_identifier:
				case Token.mp_integer_lit:
				case Token.mp_not:
					Factor(ref expression_rec);				
					Semantic_rec factorTail_rec = new Semantic_rec();
					FactorTail(ref factorTail_rec);				
					if(factorTail_rec.Type != SymbolTypes.Undefined.GetHashCode() && expression_rec.Type != factorTail_rec.Type)
					{
						Console.WriteLine("Type mismatch error on line {0}", scanner.GetLine());
					}
					break;
				default:
					Console.WriteLine("Parse error: Expected ')', an identifier or a number but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void TermTail(ref Semantic_rec expression_rec){
			switch(currentToken){
				case Token.mp_plus:
				case Token.mp_minus:
				case Token.mp_or:
					Semantic_rec addingOp_rec = new Semantic_rec();
					AddingOperator(ref addingOp_rec);				
					Term(ref expression_rec);
					analyzer.gen_AddOp(addingOp_rec);
					Semantic_rec termTail_rec = new Semantic_rec();
					TermTail(ref termTail_rec);
				
					if(termTail_rec.Type == SymbolTypes.Undefined.GetHashCode() || expression_rec.Type == termTail_rec.Type)
					{}
					else
					{
						Console.WriteLine("Type mismatch error on line {0}", scanner.GetLine());
					}
					break;
				case Token.mp_comma:
				case Token.mp_scolon:
				case Token.mp_rparen:
				case Token.mp_equal:
				case Token.mp_lthan:
				case Token.mp_gthan:
				case Token.mp_lequal:
				case Token.mp_gequal:
				case Token.mp_nequal:
				case Token.mp_do:
				case Token.mp_downto:	
				case Token.mp_else:
				case Token.mp_end:
				case Token.mp_then:
				case Token.mp_to:
				case Token.mp_until:
					expression_rec.Type = SymbolTypes.Undefined.GetHashCode();
					break;
				default:
					Console.WriteLine("Parse error: Expected '+', '-', 'or', ',', ';', ')', '=', '<', '>', '<=', '>=', '<>', 'do', 'downto', 'else', 'end', 'then', 'to' or 'until' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void Type(ref int type){
			switch(currentToken){
				case Token.mp_integer:
					type = SymbolTypes.Integer.GetHashCode();
					Match(Token.mp_integer);
					break;
				case Token.mp_float:
					type = SymbolTypes.Float.GetHashCode();
					Match(Token.mp_float);
					break;
				default:
					Console.WriteLine("Parse error: Expected 'Integer' or 'Float' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}

		private void ValueParameterSection(ref ParamObject paramObject){
			switch(currentToken){
				case Token.mp_identifier:
					IdentifierList(ref paramObject.IdList);
					Match(Token.mp_colon);
					Type(ref paramObject.Type);
					break;
				default:
					Console.WriteLine("Parse error: Expected an identifier but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void VariableDeclaration(){
			switch(currentToken){
				case Token.mp_identifier:
					VariableObject variableObject = new VariableObject();				
					IdentifierList(ref variableObject.IdList);
					Match(Token.mp_colon);
					Type(ref variableObject.Type);					
					foreach(string identifier in variableObject.IdList)
					{
						topTable.AddSymbol(SymbolKind.Variable.GetHashCode(), identifier, variableObject.Type);
					}
					break;
				default:
					Console.WriteLine("Parse error: Expected an identifier but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void VariableDeclarationPart(){
			switch(currentToken){
				case Token.mp_var:
					Match(Token.mp_var);
					VariableDeclaration();
					Match(Token.mp_scolon);
					VariableDeclarationTail();
					break;
				case Token.mp_begin:
				case Token.mp_function:
				case Token.mp_procedure:
					break;
				default:
					Console.WriteLine("Parse error: Expected 'var', 'begin', 'function' or 'procedure' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void VariableDeclarationTail(){
			switch(currentToken){
				case Token.mp_identifier:
					VariableDeclaration();
					Match(Token.mp_scolon);
					VariableDeclarationTail();
					break;
				case Token.mp_begin:
				case Token.mp_function:
				case Token.mp_procedure:
					break;
				default:
					Console.WriteLine("Parse error: Expected 'begin', 'function', 'procedure' or an identifier but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void VariableIdentifier(ref string identifier){
			switch(currentToken){
				case Token.mp_identifier:
					identifier = scanner.GetLexeme();
					Match(Token.mp_identifier);
					break;
				default:
					Console.WriteLine("Parse error: Expected an identifier but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void VariableParameterSection(ref ParamObject paramObject){
			switch(currentToken){
				case Token.mp_var:
					Match(Token.mp_var);
					IdentifierList(ref paramObject.IdList);
					Match(Token.mp_colon);
					Type(ref paramObject.Type);
					break;
				default:
					Console.WriteLine("Parse error: Expected 'var' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void WhileStatement(){
			switch(currentToken){
				case Token.mp_while:
					Match(Token.mp_while);				
					var checkLabel = "";
					var finishLabel = "";
					analyzer.gen_WhileStart(ref checkLabel, ref finishLabel);
					BooleanExpression();
					analyzer.gen_WhilePostCheck(finishLabel);				
					Match(Token.mp_do);
					Statement();
					analyzer.gen_WhileEnd(checkLabel, finishLabel);
					break;
				default:
					Console.WriteLine("Parse error: Expected 'while' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void WriteParameter(){
			switch(currentToken){
				case Token.mp_lparen:
				case Token.mp_plus:
				case Token.mp_minus:
				case Token.mp_identifier:
				case Token.mp_not:
				case Token.mp_integer_lit:
					int temp = 0;
					OrdinalExpression(ref temp);
					analyzer.gen_WriteStatement();
					break;
				default:
					Console.WriteLine("Parse error: Expected '(', '+', '-', 'not', a number or an identifier but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}

		private void WriteParameterTail(){
			switch(currentToken){
				case Token.mp_comma:
					Match(Token.mp_comma);
					WriteParameter();
					WriteParameterTail();
					break;
				case Token.mp_rparen:
					break;
				default:
					Console.WriteLine("Parse error: Expected ',' or ')' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}

		private void WriteStatement(){
			switch(currentToken){
				case Token.mp_write:
					Match(Token.mp_write);
					Match(Token.mp_lparen);
					WriteParameter();
					WriteParameterTail();
					Match (Token.mp_rparen);
					break;
				default:
					Console.WriteLine("Parse error: Expected 'write' but found '{0}'. Error occurred on line {1} and column {2}.", currentToken, scanner.GetLine(), scanner.GetColumn());
					break;
			}
		}
		
		private void Match(string matching){
			if(matching.Equals(currentToken))
			{
				currentToken = scanner.GetToken();
			}
			else
				throw new Exception("No match to " + matching + " for " + currentToken + " " + scanner.GetLexeme());				
		}
		
		#endregion parser methods
		
		#region private methods
		
		private void NewFunctionOrProcedureTable(ProcOrFuncObject procOrFuncObject, string label)
		{
			SymbolTable tempTable = new SymbolTable();
			Symbol tempSymbol = new Symbol(procOrFuncObject.ProcedurOrFunction, procOrFuncObject.ProcedureOrFunctionId, procOrFuncObject.Type);
			Symbol tempParameter;
			tempSymbol.Label = label;
		
			tempTable.Name = procOrFuncObject.ProcedureOrFunctionId;
			tempTable.prevTable = topTable;
			tempTable.Level = topTable.Level + 1;
			
			foreach(ParamObject tempParam in procOrFuncObject.Params)
			{
				foreach(string id in tempParam.IdList)
				{
					tempSymbol.Params.Add(new Parameter(tempParam.Type, tempParam.IsValueParam ? ParameterMode.Value.GetHashCode() : ParameterMode.Variable.GetHashCode()));
					tempParameter = new Symbol(SymbolKind.Parameter.GetHashCode(), id, tempParam.Type);
					tempParameter.ParameterMode = tempParam.IsValueParam ? ParameterMode.Value.GetHashCode() : ParameterMode.Variable.GetHashCode();
					tempTable.AddSymbol(tempParameter);
				}
			}
		
			topTable.AddSymbol(tempSymbol);
			topTable = tempTable;
			topTable.AdditionalOffset = 1;
		}
		
		private void PrintTable()
		{
			Symbol tempSymbol;
			var tempTable = topTable;
			string paramList = "";
			while(tempTable != null)
			{
				writer1.WriteLine(tempTable.Name + " Level: " + tempTable.Level);
				writer1.WriteLine("--------------------------------------------------");
				writer1.WriteLine("Lexeme Kind Type Offset Parameters");
				writer1.WriteLine("--------------------------------------------------");
				foreach(System.Collections.Generic.KeyValuePair<string, Symbol> pair in tempTable.Implementation)
				{
					tempSymbol = pair.Value;
					paramList = "";
					if(tempSymbol.Params.Count > 0)
					{
						foreach(Parameter param in tempSymbol.Params)
						{
							paramList += Enum.GetName(typeof(SymbolTypes), param.Type) + ", ";
						}
					}
					else
						paramList = "No parameters";
						
					writer1.WriteLine("{0} {1} {2} {3} Parameters: {4}", tempSymbol.Lexeme, Enum.GetName(typeof(SymbolKind), tempSymbol.Kind), Enum.GetName(typeof(SymbolTypes), tempSymbol.Type), tempSymbol.Offset, paramList);
				}
				writer1.WriteLine("--------------------------------------------------\n");
				tempTable = tempTable.prevTable;
			}
		}
		
		#endregion private methods
		
		#region properties
		
		private string currentToken;
		private mp.Scanner.Scanner scanner;
		private mp.Analyzer.Analyzer analyzer;
		public SymbolTable topTable;
		private StreamWriter writer1;
		
		#endregion properties
	}
}

